りおんクロニクル


SQLite × ログ設計|監査ログ・操作ログの完全ガイド【2026年版】

Home【2026年版】C# / .NET入門と実践ガイド|基礎・業務アプリ開発・SQLite連携まで体系的に解説

業務アプリでは、「誰が・いつ・何をしたか」を残す監査ログや操作ログが必須です。 SQLiteは軽量で高速なためログ用途に向いていますが、 設計を誤ると肥大化・ロック・パフォーマンス低下が起きます。 この記事では、SQLiteでログを安全かつ高速に扱うための実務パターンをまとめます。

この記事でわかること
・監査ログ・操作ログの違いと設計方針
・SQLiteログテーブルの最適な構造
・高速INSERTの実装(C#)
・ローテーション(世代管理)
・セキュリティとバックアップ
・業務アプリ向けベストプラクティス

1. 監査ログと操作ログの違い

■ 監査ログ(Audit Log)

■ 操作ログ(Operation Log)

用途に応じてテーブルを分けるのが基本です。

2. SQLiteログテーブルの設計

■ 監査ログテーブル例(追記専用)

CREATE TABLE AuditLogs (
    Id        INTEGER PRIMARY KEY AUTOINCREMENT,
    UserId    TEXT NOT NULL,
    Action    TEXT NOT NULL,
    Detail    TEXT,
    CreatedAt TEXT NOT NULL
);

■ 操作ログテーブル例(ローテーション前提)

CREATE TABLE OperationLogs (
    Id        INTEGER PRIMARY KEY AUTOINCREMENT,
    Level     TEXT NOT NULL,   -- INFO / WARN / ERROR
    Message   TEXT NOT NULL,
    CreatedAt TEXT NOT NULL
);

■ 設計ポイント

3. C#での高速INSERT(ログはとにかく速さが命)

ログは頻繁に書き込まれるため、 高速・非同期・短時間トランザクションが重要です。

■ 最速パターン:プリペアドステートメント+トランザクション

using Microsoft.Data.Sqlite;

public class LogWriter
{
    private readonly SqliteConnection _con;
    private readonly SqliteCommand _cmd;

    public LogWriter(string cs)
    {
        _con = new SqliteConnection(cs);
        _con.Open();

        _cmd = _con.CreateCommand();
        _cmd.CommandText = @"
            INSERT INTO OperationLogs (Level, Message, CreatedAt)
            VALUES (@level, @msg, @createdAt);
        ";
        _cmd.Parameters.Add("@level", SqliteType.Text);
        _cmd.Parameters.Add("@msg", SqliteType.Text);
        _cmd.Parameters.Add("@createdAt", SqliteType.Text);

        _cmd.Prepare();
    }

    public void Write(string level, string message)
    {
        using var tran = _con.BeginTransaction();

        _cmd.Parameters["@level"].Value = level;
        _cmd.Parameters["@msg"].Value = message;
        _cmd.Parameters["@createdAt"].Value = DateTime.UtcNow.ToString("o");

        _cmd.ExecuteNonQuery();

        tran.Commit();
    }
}

プリペアドステートメント+トランザクションで 1件あたりのINSERTが圧倒的に高速になります。

4. 非同期ログ(UIフリーズ防止)

WPF/WinFormsでは、ログ書き込みをUIスレッドで行うと固まるため、 バックグラウンドキュー方式が最適です。

■ BlockingCollection を使ったログキュー

private readonly BlockingCollection<string> _queue = new();

public void StartWorker()
{
    Task.Run(() =>
    {
        foreach (var msg in _queue.GetConsumingEnumerable())
        {
            Write("INFO", msg); // 上のWriteメソッド
        }
    });
}

public void Log(string message)
{
    _queue.Add(message);
}

これでログは非同期で安全に直列化され、 database is locked の発生も防げます。

5. ローテーション(古いログの削除)

操作ログは無限に増えるため、 一定期間で削除 or 別ファイルに退避する必要があります。

■ 例:30日より古いログを削除

DELETE FROM OperationLogs
WHERE CreatedAt < datetime('now', '-30 days');

■ C#で定期実行

public void Cleanup()
{
    using var con = new SqliteConnection(_cs);
    con.Open();

    var sql = @"
        DELETE FROM OperationLogs
        WHERE CreatedAt < datetime('now', '-30 days');
    ";

    new SqliteCommand(sql, con).ExecuteNonQuery();
}

監査ログは削除しないため、 監査ログと操作ログは必ずテーブルを分けるべきです。

6. JSONログで柔軟性を高める

Detail を JSON にすると、 スキーマ変更なしで柔軟にログ内容を増やせます。

■ JSONログ例

{
  "Before": { "Name": "Taro" },
  "After":  { "Name": "Jiro" },
  "Ip": "192.168.1.10"
}

SQLiteは JSON 型を持ちませんが、TEXT で問題なく扱えます。

7. セキュリティ:ログは機密情報の宝庫

ログには個人情報・操作履歴・エラー内容が含まれるため、 セキュリティ対策が必須です。

■ 注意点

8. 業務アプリ向けベストプラクティス

まとめ:SQLiteのログ設計は“速さ・安全性・運用”のバランスが鍵

「とりあえずINSERTしておけばいい」ではなく、 「壊れず・速く・安全に残す」ログ設計が業務アプリの品質を決めます。 この記事のパターンをベースに、あなたのアプリに最適なログ基盤を構築してみてください。

前のページ  次のページ